﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Text;
using System.Threading;
using Tsanie.Util;
using Tsanie.Util.Components;
using System.Reflection;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;

namespace Tsanie.Image2Cmt {
    public partial class FrmMain : Form {

        #region - 静态/版本、标题 -
        private static readonly string TITLE;

        static FrmMain() {
            Version ver = Assembly.GetExecutingAssembly().GetName().Version;
            string build = ver.Build.ToString("D4");
            string revision = ver.Revision.ToString("D4");
            int result = 0;
            int month = int.Parse(build.Substring(0, 2));
            for (int i = 7; i < month; i++) {
                if (i == 7) {
                    result += GetDaysOfMonth(7) - 27;
                } else {
                    result += GetDaysOfMonth(i);
                }
            }
            int day = int.Parse(build.Substring(2));
            result += (day > 27 ? day - 27 : (result > 0 ? day : 0));
            TITLE = string.Format("图像 → 弹幕 [mode8] v{0}.{1} beta{2}{3}",
                ver.Major,
                ver.Minor,
                (result > 0 ? result.ToString() : ""),
                revision.Substring(0, 3));
        }
        private static int GetDaysOfMonth(int month) {
            switch (month) {
                case 1:
                case 3:
                case 5:
                case 7:
                case 8:
                case 10:
                case 12:
                    return 31;
                case 2:
                    return 28;
                case 4:
                case 6:
                case 9:
                case 11:
                    return 30;
            }
            return 0;
        }
        #endregion

        #region  - 私有字段 -
        private bool isRunning = false;
        private ITaskbarList3 taskbar = null;
        private Point lastPoint = Point.Empty;
        private ValueBitmap bitmap = null;

        private int tmp;
        #endregion

        #region - 窗体事件/构造 -
        public FrmMain() {
            InitializeComponent();
            canvas.AllowDrop = true;
            this.Text = TITLE;
            this.Icon = Tsanie.Image2Cmt.Properties.Resources.image2cmt;
        }

        private void FrmMain_Shown(object sender, EventArgs e) {
            // 任务栏
            if (VistaStuff.IsWin7)
                taskbar = (ITaskbarList3)new ProgressTaskbar();
        }

        private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) {
            isRunning = false;
            if (bitmap != null) {
                bitmap.Stop();
                bitmap.Dispose();
            }
            taskbar = null;
        }
        #endregion

        #region - UI控制 -
        private void SetState(TBPFLAG flag) {
            switch (flag) {
                case TBPFLAG.TBPF_INDETERMINATE:
                    progressBar.Style = ProgressBarStyle.Marquee;
                    progressBar.Visible = true;
                    buttonStop.Visible = true;
                    break;
                case TBPFLAG.TBPF_NORMAL:
                    progressBar.Style = ProgressBarStyle.Blocks;
                    progressBar.Visible = true;
                    buttonStop.Visible = true;
                    break;
                default:
                    progressBar.Visible = false;
                    buttonStop.Visible = false;
                    break;
            }
            if (taskbar != null)
                taskbar.SetProgressState(this.Handle, flag);
        }
        private void SetValue(int completed, int total) {
            if (completed > total)
                return;
            if (total != progressBar.Maximum)
                progressBar.Maximum = total;
            progressBar.Value = completed;
            if (taskbar != null)
                taskbar.SetProgressValue(this.Handle, (ulong)completed, (ulong)total);
        }
        private void EnabledUI(bool enable, string title) {
            panel1.Enabled = enable;
            this.Text = TITLE + " - " + title;
        }
        #endregion

        #region - UI事件 -

        private void Input_ValueChanged(object sender, EventArgs e) {
            if (isRunning || bitmap == null || bitmap.BitmapData.Length == 0)
                return;

            new Thread(() => {
                if (isRunning)
                    return;
                CreatePreview();
                if (lastPoint.X != inputPointWidth.Value ||
                    lastPoint.Y != inputPointHeight.Value) {
                    Input_ValueChanged(null, null);
                    return;
                }
                // 信息
                if (bitmap.State == WorkState.Done) {
                    this.SafeRun(delegate {
                        SetState(TBPFLAG.TBPF_INDETERMINATE);
                    });

                    this.canvas.CreateDone += new EventHandler(delegate {
                        this.SafeRun(delegate {
                            EnabledUI(true, string.Format("生成完毕，一共 {0} 色，{1} 条线段", bitmap.Layers.Keys.Count, bitmap.LineCount));
                            SetState(TBPFLAG.TBPF_NOPROGRESS);
                        });
                    });
                    this.canvas.Bitmap = bitmap;
                }
            }) { Name = "threadCreatePreview" }.Start();
        }
        private void canvas_DragEnter(object sender, DragEventArgs e) {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
        }
        private void canvas_DragDrop(object sender, DragEventArgs e) {
            string file = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
            // 读取像素信息
            EnabledUI(false, "正在读取图片像素信息...");

            try {
                Bitmap bmp = new Bitmap(file);
                new Thread(() => {
                    this.bitmap = CreatePoints(bmp);
                    bmp.Dispose();
                    bmp = null;
                    if (this.bitmap.State == WorkState.Done) {
                        this.canvas.Reset();
                        Input_ValueChanged(null, null);
                    }
                }) { Name = "threadLoadPicture" }.Start();
            } catch (Exception ex) {
                EnabledUI(true, "读取图片错误：" + ex.Message);
            }
        }
        private void buttonOut_Click(object sender, EventArgs e) {
            if (bitmap.Layers == null || bitmap.Layers.Count == 0) {
                MessageBox.Show(this, "无弹幕可供生成！", "输出", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            string cmd = (sender as Button).Name;
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.DefaultExt = ".txt";
            dialog.FileName = null;
            dialog.Title = "选择输出文件";
            dialog.Filter = (cmd == "buttonOut7"
                ? "弹幕文件 (*.xml)|*.xml|所有文件|*.*"
                : "文本文件 (*.txt)|*.txt|所有文件|*.*");
            if (dialog.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) {
                new Thread(() => {
                    switch (cmd) {
                        case "buttonOut":
                            OutputFile(dialog.FileName);
                            break;
                        case "buttonOut2":
                            OutputFile2(dialog.FileName);
                            break;
                        case "buttonOut7":
                            OutputFileMode7(dialog.FileName);
                            break;
                    }
                }) { Name = "threadOutputFile" }.Start();
            }
        }
        private void buttonStop_Click(object sender, EventArgs e) {
            if (bitmap == null)
                return;
            bitmap.Stop();
            isRunning = false;
            EnabledUI(true, "被用户中止 ……");
            SetState(TBPFLAG.TBPF_NOPROGRESS);
        }

        #endregion

        #region - 私有方法 -

        private ValueBitmap CreatePoints(Bitmap bmp) {
            isRunning = true;
            int total = bmp.Height;
            this.SafeRun(delegate {
                SetValue(0, total);
                SetState(TBPFLAG.TBPF_NORMAL);
            });
            ValueBitmap vb = new ValueBitmap();
            System.Timers.Timer timer = new System.Timers.Timer(60);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object obj, System.Timers.ElapsedEventArgs arg) {
                if (!isRunning) {
                    timer.Stop();
                    timer.Dispose();
                    return;
                }
                this.SafeRun(delegate { SetValue(vb.Current, total); });
            });
            timer.Start();
            // 解析
            vb.ParseBitmap(bmp);
            timer.Stop();
            timer.Dispose();
            this.SafeRun(delegate {
                SetValue(vb.Current, total);
                SetState(TBPFLAG.TBPF_NOPROGRESS);
            });
            isRunning = false;
            return vb;
        }
        private void CreatePreview() {
            isRunning = true;
            int stepY = (int)inputPointHeight.Value;
            int stepX = (int)inputPointWidth.Value;
            int ignores = (int)inputColorIgnore.Value;
            this.lastPoint = new Point(stepX, stepY);
            int total = bitmap.Palette.Length;
            this.SafeRun(delegate {
                EnabledUI(false, "开始压缩调色板...");
                SetValue(0, total);
                SetState(TBPFLAG.TBPF_NORMAL);
            });
            System.Timers.Timer timer = new System.Timers.Timer(100);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object obj, System.Timers.ElapsedEventArgs arg) {
                if (!isRunning) {
                    timer.Stop();
                    timer.Dispose();
                    return;
                }
                this.SafeRun(delegate { SetValue(bitmap.Current, total); });
            });
            timer.Start();
            // Create
            bitmap.CreatePreview(stepX, stepY, ignores, (count) => {
                this.SafeRun(delegate {
                    EnabledUI(false, string.Format("调色板一共 {0} 色，开始生成图层...", count));
                    SetValue(0, total);
                    SetState(TBPFLAG.TBPF_NORMAL);
                });
                total = bitmap.Height / stepY;
            });

            timer.Stop();
            timer.Dispose();
            this.SafeRun(delegate {
                SetValue(bitmap.Current, total);
                SetState(TBPFLAG.TBPF_NOPROGRESS);
            });
            isRunning = false;
        }
        private void OutputFile(string filename) {
            isRunning = true;
            int total = bitmap.LineCount;
            this.SafeRun(delegate {
                SetValue(0, total);
                SetState(TBPFLAG.TBPF_NORMAL);
                EnabledUI(false, "正在输出文件...");
            });
            int ptwidth = (int)inputPointWidth.Value;
            int ptheight = (int)inputPointHeight.Value;
            int n = 0;
            this.tmp = 0;
            System.Timers.Timer timer = new System.Timers.Timer(100);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object obj, System.Timers.ElapsedEventArgs arg) {
                if (!isRunning) {
                    timer.Stop();
                    timer.Dispose();
                    this.SafeRun(delegate {
                        SetState(TBPFLAG.TBPF_NOPROGRESS);
                        EnabledUI(true, "用户中止输出文件 ……");
                    });
                    return;
                }
                this.SafeRun(delegate { SetValue(this.tmp, total); });
            });
            timer.Start();
            StringBuilder builder = new StringBuilder();
            builder.Append("var layers=[\n");
            foreach (KeyValuePair<Color, ColorLayer> pair in bitmap.Layers) {
                builder.Append("{c:0x" + pair.Key.FormatColor() + ",l:[");
                int lastY = -1, lastX = 0;
                foreach (Line line in pair.Value.lines) {
                    if (!isRunning) {
                        timer.Stop();
                        timer.Dispose();
                        return;
                    }
                    // 线段、点
                    if (lastY != line.index) {
                        if (lastY >= 0) {
                            builder.Length--;
                            builder.Append("],");
                        }
                        builder.Append(string.Format("[{0},", line.index));
                        lastY = line.index;
                        lastX = 0;
                    }
                    if (lastX != line.offset) {
                        builder.Append((lastX - line.offset).ToString() + ",");
                        lastX = line.offset;
                    }
                    int width = (line.end - line.offset);
                    builder.Append(width.ToString() + ",");
                    lastX += width;

                    this.tmp = ++n;
                }
                builder.Length--;
                builder.Append("]]},");
            }
            builder.Length--;
            builder.Append("];\n");


            System.IO.StreamWriter writer = new System.IO.StreamWriter(filename);
            writer.Write(builder.ToString());
            writer.Write("var g = $.createShape(" + outParam.Text + ");\n");
            writer.Write("var n,l,y,x,nowx,nowy,layer,lines;\n");
            writer.Write("for(n=0;n<layers.length;n++){\n");
            writer.Write("layer=layers[n];\n");
            if (ptwidth > 1 || ptheight > 1) {
                writer.Write("g.graphics.lineStyle(1,layer.c,1,true,\"none\",\"none\",\"bevel\",1);\n");
            } else {
                writer.Write("g.graphics.lineStyle(1,layer.c,1,true,\"none\",\"none\",\"bevel\",1);\n");
            }
            writer.Write("for(l=0;l<layer.l.length;l++){\n");
            writer.Write("lines=layer.l[l];\n");
            writer.Write("nowx=0;nowy=lines[0]*" + ptheight + ";\n");
            writer.Write("g.graphics.moveTo(nowx,nowy);\n");
            writer.Write("for(y=1;y<lines.length;y++){\n");
            writer.Write("x=lines[y];\n");
            writer.Write("if(x<0){\n");
            writer.Write("nowx+=-x*" + ptwidth + ";\n");
            writer.Write("g.graphics.moveTo(nowx,nowy);\n");
            writer.Write("continue;\n");
            writer.Write("}\n");
            if (ptwidth > 1 || ptheight > 1) {
                writer.Write("g.graphics.beginFill(layer.c,1);\n");
                writer.Write("g.graphics.drawRect(nowx,nowy," + ptwidth + "*x," + ptheight + ");\n");
                writer.Write("nowx+=x*" + ptwidth + ";\n");
                writer.Write("g.graphics.endFill();\n");
            } else {
                writer.Write("nowx+=x;\n");
                writer.Write("g.graphics.lineTo(nowx,nowy);\n");
            }
            writer.Write("}\n}\n}");
            writer.Flush();
            writer.Close();
            writer.Dispose();

            timer.Stop();
            timer.Dispose();
            this.SafeRun(delegate {
                SetState(TBPFLAG.TBPF_NOPROGRESS);
                EnabledUI(true, "保存完毕");
            });
            isRunning = false;
        }
        private void OutputFile2(string filename) {
            isRunning = true;
            int total = bitmap.LineCount;
            this.SafeRun(delegate {
                SetValue(0, total);
                SetState(TBPFLAG.TBPF_NORMAL);
                EnabledUI(false, "正在输出文件...");
            });
            int n = 0;
            this.tmp = 0;
            System.Timers.Timer timer = new System.Timers.Timer(100);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object obj, System.Timers.ElapsedEventArgs arg) {
                if (!isRunning) {
                    timer.Stop();
                    timer.Dispose();
                    this.SafeRun(delegate {
                        SetState(TBPFLAG.TBPF_NOPROGRESS);
                        EnabledUI(true, "用户中止输出文件 ……");
                    });
                    return;
                }
                this.SafeRun(delegate { SetValue(this.tmp, total); });
            });
            timer.Start();
            StringBuilder builder = new StringBuilder();
            builder.Append("var layers=[\n");
            foreach (KeyValuePair<Color, ColorLayer> pair in bitmap.Layers) {
                builder.Append("{c:0x" + pair.Key.FormatColor() + ",l:[");
                int lastY = -1, lastX = 0;
                foreach (Line line in pair.Value.lines) {
                    if (!isRunning) {
                        timer.Stop();
                        timer.Dispose();
                        return;
                    }
                    // 线段、点
                    if (lastY != line.index) {
                        if (lastY >= 0) {
                            builder.Length--;
                            builder.Append("],");
                        }
                        builder.Append(string.Format("[{0},", line.index));
                        lastY = line.index;
                        lastX = 0;
                    }
                    if (lastX != line.offset) {
                        builder.Append((lastX - line.offset).ToString() + ",");
                        lastX = line.offset;
                    }
                    int width = (line.end - line.offset);
                    builder.Append(width.ToString() + ",");
                    lastX += width;

                    this.tmp = ++n;
                }
                builder.Length--;
                builder.Append("]]},");
            }
            builder.Length--;
            builder.Append("];\n");


            System.IO.StreamWriter writer = new System.IO.StreamWriter(filename);
            writer.Write(builder.ToString());
            writer.Write("var g = $.createShape(" + outParam.Text + ");\n");
            writer.Write("var n,l,y,x,nowx,nowy,layer,lines;\n");
            writer.Write("for(n=0;n<layers.length;n++){\n");
            writer.Write("layer=layers[n];\n");
            writer.Write("g.graphics.lineStyle(1,layer.c,1,true,\"none\",\"none\",\"bevel\",1);\n");
            writer.Write("for(l=0;l<layer.l.length;l++){\n");
            writer.Write("lines=layer.l[l];\n");
            writer.Write("nowx=0;nowy=lines[0];\n");
            writer.Write("g.graphics.moveTo(nowx,nowy);\n");
            writer.Write("for(y=1;y<lines.length;y++){\n");
            writer.Write("x=lines[y];\n");
            writer.Write("if(x<0){\n");
            writer.Write("nowx+=-x;\n");
            writer.Write("g.graphics.moveTo(nowx,nowy);\n");
            writer.Write("continue;\n");
            writer.Write("}\n");
            writer.Write("nowx+=x;\n");
            writer.Write("g.graphics.lineTo(nowx,nowy);\n");
            writer.Write("}\n}\n}");
            writer.Flush();
            writer.Close();
            writer.Dispose();

            timer.Stop();
            timer.Dispose();
            this.SafeRun(delegate {
                SetState(TBPFLAG.TBPF_NOPROGRESS);
                EnabledUI(true, "保存完毕");
            });
            isRunning = false;
        }
        private void OutputFileMode7(string filename) {
            isRunning = true;
            int total = bitmap.LineCount;
            this.SafeRun(delegate {
                SetValue(0, total);
                SetState(TBPFLAG.TBPF_NORMAL);
                EnabledUI(false, "正在输出文件...");
            });
            int n = 0;
            this.tmp = 0;
            // 参数
            decimal playTime = 0.0M;
            int fontSize = 2;
            decimal lifeTime = 8.0M;
            int maxLength = 280;
            int offsetX = 0;
            int offsetY = 0;
            this.SafeRun(delegate {
                playTime = outPlayTime.Value;
                fontSize = int.Parse(outFontsize.Text);
                lifeTime = outLifeTime.Value;
                maxLength = (int)outMaxLength.Value;
                offsetX = (int)outOffsetX.Value;
                offsetY = (int)outOffsetY.Value;
            });

            string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            int msgEndLength = 57;
            // 输出记录参数
            int count = 0;
            int wrapped = 0;

            System.Timers.Timer timer = new System.Timers.Timer(100);
            timer.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object obj, System.Timers.ElapsedEventArgs arg) {
                if (!isRunning) {
                    timer.Stop();
                    timer.Dispose();
                    this.SafeRun(delegate {
                        SetState(TBPFLAG.TBPF_NOPROGRESS);
                        EnabledUI(true, "用户中止输出文件 ……");
                    });
                    return;
                }
                this.SafeRun(delegate { SetValue(this.tmp, total); });
            });
            timer.Start();
            System.IO.StreamWriter writer = new System.IO.StreamWriter(filename);
            writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            writer.WriteLine("<information>");
            foreach (KeyValuePair<Color, ColorLayer> pair in bitmap.Layers) {
                int lastY = -1, lastX = 0;
                int x = offsetX, y = offsetY;
                int charCount = 0;
                Color c = pair.Key;
                foreach (Line line in pair.Value.lines) {
                    if (!isRunning) {
                        timer.Stop();
                        timer.Dispose();
                        return;
                    }
                    // 线段、点
                    if (lastY != line.index) {
                        if (lastY >= 0) {
                            WriteEnd(writer, x, y, time);
                            count++;
                        }
                        lastX = line.offset;
                        lastY = line.index;
                        y = lastY * fontSize + offsetY;
                        x = line.offset * fontSize + offsetX;

                        charCount = WriteBegin(writer, playTime, fontSize, c.ToRgb(), x, y, lifeTime);
                    }
                    if (lastX != line.offset) {
                        for (int i = 0; i < line.offset - lastX; i++) {
                            if (charCount + msgEndLength + 1 > maxLength) {
                                // 弹幕长度过长，截断
                                WriteEnd(writer, x, y, time);
                                count++;
                                wrapped++;
                                x += i * fontSize + offsetX;
                                charCount = WriteBegin(writer, playTime, fontSize, c.ToRgb(), x, y, lifeTime);
                            }
                            writer.Write('　');
                            charCount++;
                        }
                        lastX = line.offset;
                    }
                    int length = (line.end - line.offset);
                    for (int i = 0; i < length; i++) {
                        if (charCount + msgEndLength + 1 > maxLength) {
                            // 弹幕长度过长，截断
                            WriteEnd(writer, x, y, time);
                            count++;
                            wrapped++;
                            x += i * fontSize + offsetX;
                            charCount = WriteBegin(writer, playTime, fontSize, c.ToRgb(), x, y, lifeTime);
                        }
                        writer.Write('█');
                        charCount++;
                    }
                    lastX += length;

                    this.tmp = ++n;
                }
                WriteEnd(writer, x, y, time);
                count++;
            }
            writer.WriteLine("</information>");

            writer.Flush();
            writer.Close();
            writer.Dispose();

            timer.Stop();
            timer.Dispose();
            this.SafeRun(delegate {
                SetState(TBPFLAG.TBPF_NOPROGRESS);
                EnabledUI(true, "保存完毕");
                MessageBox.Show(this, string.Format("保存完毕，一共 {0} 条弹幕\n其中 {1} 条是因过长截断而产生。", count, wrapped), "输出弹幕",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            });
            isRunning = false;
        }
        private void WriteEnd(StreamWriter writer, int x, int y, string time) {
            writer.Write("\",\"0\",\"0\",\"{0}\",\"{1}\"", x, y);
            writer.WriteLine(",\"500\",\"0\",\"false\",\"MS UI Gothic\"]]]></message>");
            writer.WriteLine("\t<times>{0}</times>", time);
            writer.WriteLine("</data>");
        }
        private int WriteBegin(StreamWriter writer, decimal playTime, int fontSize, int color, int x, int y, decimal lifeTime) {
            writer.WriteLine("<data>");
            writer.WriteLine("\t<playTime>0</playTime>");
            writer.Write("\t<message fontsize=\"{0}\" color=\"{1}\" mode=\"7\">", fontSize, color);
            string msg = string.Format("<![CDATA[[\"{0}\",\"{1}\",\"1-1\",\"{2}\",\"", x, y, lifeTime);
            writer.Write(msg);
            return msg.Length;
        }

        #endregion

    }

    /// <summary>
    /// 扩展工具类
    /// </summary>
    static class Utility {
        /// <summary>
        /// UI线程安全执行委托
        /// </summary>
        /// <param name="control">需要在其上执行的控件</param>
        /// <param name="invoker">委托</param>
        public static void SafeRun(this Control control, MethodInvoker invoker) {
            try {
                if (control != null && control.InvokeRequired) {
                    control.Invoke(invoker);
                } else {
                    invoker();
                }
            } catch { }
        }
        /// <summary>
        /// UI线程安全执行委托（无阻塞）
        /// </summary>
        /// <param name="control">需要在其上执行的控件</param>
        /// <param name="invoker">委托</param>
        public static void SafeBeginRun(this Control control, MethodInvoker invoker) {
            if (control != null) {
                control.BeginInvoke(invoker);
            } else {
                new Thread(new ThreadStart(invoker)).Start();
            }
        }
    }
}
